package qr.server.admin;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import qr.core.Game;
import qr.core.Location;
import qr.server.managers.TransactionGateway;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

@SuppressWarnings("serial")
public class AddLocationScreen extends ApplicationScreen {
	
	private Game game;
	private Location location;
	private ExportToPdf generatePDF; 
	
	private JPanel attributes;
	private JPanel actions;
	
	private JPicturePanel image = new JPicturePanel();
	
	private JTextField name;
	private JTextField clue;
	private JTextArea desc;
	
	private JButton confirm;
	private JButton cancel;
	private JButton generate;
	
	public AddLocationScreen(ApplicationWindow parent) {
		super(parent);
		
		this.setLayout(new BorderLayout());
		
		// Create panels
		this.attributes = new JPanel(new FlowLayout());
		this.actions = new JPanel(new FlowLayout());
		
		// Create components
		this.name = new JTextField(15);
		this.clue = new JTextField(32);
		this.desc = new JTextArea();
		this.confirm = new JButton("Confirm");
		this.cancel = new JButton("Cancel");
		this.generate = new JButton("Generate QR Code");
		
		// Place components
		this.attributes.add(new JLabel("Name: "));
		this.attributes.add(this.name);
		this.attributes.add(new JLabel("Clue: "));
		this.attributes.add(this.clue);
		
		this.actions.add(this.confirm);
		this.actions.add(this.cancel);
		this.actions.add(this.generate);
		
		this.add(attributes, BorderLayout.NORTH);
		this.add(desc, BorderLayout.CENTER);
		this.add(actions, BorderLayout.SOUTH);

		this.confirm.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				confirmClicked(e);
			}
		});
		
		this.cancel.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				cancelClicked(e);
			}
		});
		
		this.generate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				generateClicked(e);
			}
		});
	}

	public AddLocationScreen(ApplicationWindow parent, Object[] objs) {
		this(parent);
		if(objs.length >= 1 && objs[0] instanceof Game) {
			this.game = (Game) objs[0];
			if(objs.length >= 2 && objs[1] instanceof Location) {
				this.location = (Location) objs[1];
			} else {
				this.location = new Location();
				System.err.println("AddLocationScreen: Was not given a valid location.\n" +
					"Creating a new location instead.");
			}
			
			// Update fields to reflect current location info
			this.name.setText(location.getName());
			this.clue.setText(location.getClue());
			this.desc.setText(location.getInformation());
			
		} else {
			System.err.println("AddLocationScreen: Was not given a game, so there's nothing to add to.\n" +
					"Returning to HomeScreen.");
			switchTo("HomeScreen");
		}
	}

	/**
	 * Handler for click event of the confirm button.
	 * 
	 * @param e Event object
	 */
	private void confirmClicked(ActionEvent e) {
		TransactionGateway db = TransactionGateway.getInstance();
		
		// TODO reduce complexity here...
		
		// First persist the location.
		if(db.isManaged(location)) {
			db.startTransaction();
		}
		location.setName(name.getText());
		location.setClue(clue.getText());
		location.setInformation(desc.getText());
		if(db.isManaged(location)) {
			db.endTransaction();
		} else {
			db.persist(location);
			// Then persist the game
			if(db.isManaged(game)) {
				db.startTransaction();
			}
			game.addLocation(location);
			if(db.isManaged(game)) {
				db.endTransaction();
			}
		}
		
		switchTo("CreateGameScreen", new Object[]{game});
	}
	
	/**
	 * Handler for click event of the cancel button.
	 * 
	 * @param e Event object
	 */
	private void cancelClicked(ActionEvent e) {
		switchTo("CreateGameScreen", new Object[]{game});
	}

	private void generateClicked(ActionEvent e) {
		TransactionGateway db = TransactionGateway.getInstance();
		
		// TODO reduce complexity here...
		
		// First persist the location.
		if(db.isManaged(location)) {
			db.startTransaction();
		}
		location.setName(name.getText());
		location.setClue(clue.getText());
		location.setInformation(desc.getText());
		if(db.isManaged(location)) {
			db.endTransaction();
		} else {
			db.persist(location);
			// Then persist the game
			if(db.isManaged(game)) {
				db.startTransaction();
			}
			game.addLocation(location);
			if(db.isManaged(game)) {
				db.endTransaction();
			}
		}
		
		generatePDF = new ExportToPdf(); 
		Image qrImage = generateQRCode("" + location.getId());
		this.image.setImage(qrImage);
		JFrame codeFrame = new JFrame();
		codeFrame.setLayout(new BorderLayout());
		codeFrame.add(image, BorderLayout.CENTER);
		codeFrame.setBackground(Color.WHITE);
		codeFrame.add(new JLabel("PDF File: \n" + "QRServer_QR Code for Location " + String.valueOf(location.getId())), BorderLayout.SOUTH);
		codeFrame.setSize(new Dimension(260, 300));
		codeFrame.setVisible(true);
		
		exportToPdf(qrImage, String.valueOf(location.getId()));
	}
	
	private Image generateQRCode() {
		return this.generateQRCode(Double.toString(Math.random()));
	}
	
	private Image generateQRCode(String seed) {
		QRCodeWriter writer = new QRCodeWriter();
		BitMatrix matrix = null;
		
		try {
			matrix = writer.encode(seed, BarcodeFormat.QR_CODE, 256, 256);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
		return MatrixToImageWriter.toBufferedImage(matrix);
	}
	
	private void exportToPdf(Image qrImage, String locationID) { 
		
		String currentDirectory = System.getProperty("user.dir");
		String imagePath = currentDirectory +"_" + locationID + ".jpg";
		
		//First write input image as a JPEG image in current working directory
		BufferedImage imageWriter = new BufferedImage(qrImage.getWidth(null), qrImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
		Graphics2D g = imageWriter.createGraphics();
		g.drawImage(qrImage, null, null);
		try { 
			ImageIO.write(imageWriter, "JPEG", new File(imagePath));
		} catch (IOException ioexception) { 
			System.out.println("Warning! Error in exporting image to JPEG");
		}
		
		//Get the JPEG image and convert / export as PDF. 
		generatePDF.pdfExport(imagePath, locationID);
	}
	
}

class JPicturePanel extends JPanel {
	private Image image;
	
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.drawImage(this.image, 0, 0, null);
	}
	
	public void setImage(Image image) {
		this.image = image;
	}
}